package com.wei.czz.framework.security.filter;

import com.wei.czz.common.constant.Constant;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.po.CountOptPo;
import com.wei.czz.common.utils.ContextUtils;
import com.wei.czz.common.utils.HttpUtils;
import com.wei.czz.framework.common.handler.redis.RedisStringHandler;
import com.wei.czz.framework.common.service.AsyncService;
import com.wei.czz.framework.security.manager.RequestLimitManager;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2022-05-02 09:33:26
 * className: RequestLimitingFilter 请求限制过滤器
 * version: 1.0
 * description:
 *  此拦截器的作用为控制一定时间内单 ip/用户 的请求数量，防止过度请求
 */
@Order(10)
@Component
@AllArgsConstructor
public class RequestLimitingFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger(RequestLimitingFilter.class);

    private final RequestLimitManager requestLimitManager;

    private final RedisStringHandler redisStringHandler;

    private final AsyncService asyncService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
//        log.info("进入单ip限流前置拦截方法，拦截接口：{}", request.getRequestURI());

        // 获取请求ip地址
        String requestIp = ContextUtils.get(Constant.REQUEST_IP);
        Long userId = ContextUtils.get(Constant.USER_ID);

        long now = System.currentTimeMillis();

        /*
            获取请求限制缓存
         */
        String cacheKey = requestLimitManager.getCacheKey(userId, requestIp);
        CountOptPo countOpt = redisStringHandler.get(cacheKey);
        boolean bool = Optional.ofNullable(countOpt)
                .map(CountOptPo::getUnlockTime)
                .filter(unlockTime -> now < unlockTime)
                .isPresent();
        if (bool) {
            log.info("请求源已被锁定。userId={} requestIp={} url={}", userId, requestIp, request.getRequestURI());

            // 设置返回结果
            String message = String.format("您已被系统锁定，请求被拒绝，%s秒后解锁!!!",
                    (countOpt.getUnlockTime() - now) / 1000);
            HttpUtils.setResponse(request, response, ResultEnum.REQUEST_FAIL.getCode(), message);
            return;
        }

        asyncService.execute(() -> {
            // 异步进行请求限制计算
            requestLimitManager.requestLimit(userId, requestIp);
        });

        try {

            // 请求放行
            filterChain.doFilter(request, response);

        } catch (Exception e) {
            log.error("请求限流计算下游逻辑发生异常。message={}", e.getMessage(), e);
            // 设置返回结果
            HttpUtils.setResponse(request, response, ResultEnum.SERVICE_FAIL.getCode(),
                    ResultEnum.SERVICE_FAIL.getMsg());
        }

    }

}
